#include <stdlib.h>
#include <iostream>
#include <string>
#include "util/smartptr.h"
#include "util/factory.h"
#include "util/assert.h"
#include "memory/memorypool.h"
#include "jobs/job.h"
#include "jobs/jobmanager.h"
#include "util/time.h"
#include "system/istream.h"
#include "system/filestream.h"
#include "gtl/vector.h"
#include "resource/serialiser.h"
#include <util/guid.h>

class Base
{
public:
	Base()
	{
		DebugPrint("Contructed Base");
	}

	virtual ~Base()
	{
		DebugPrint("Destructed Base");
	}
};

class Derived : public Base
{
public:
	Derived()
	{
		DebugPrint("Contructed Derived");
	}

	virtual ~Derived()
	{
		DebugPrint("Destructed Derived");
	}
};

class Derived2 : public Base
{
public:
	Derived2()
	{
		DebugPrint("Contructed Derived2");
	}

	virtual ~Derived2()
	{
		DebugPrint("Destructed Derived2");
	}
};

typedef gecko::StrongPtr<Base> BasePtr;

//REGISTER_WITH_FACTORY(Base, Derived);

class MyJob : public gecko::Job
{
public:
	MyJob(int id)
		: m_id(id)
	{

	}

	virtual void Process()
	{
		//DebugPrint("Running job %d", m_id);

		const int iterations = 1000000;

		for (int i = 0; i < iterations; i++)
		{
			float root = sqrt((float)i);
			root = 0.0f;
		}
	}

	int m_id;
};

#include <set>
#include "resource\metaclass.h"
#include "resource\iserialisable.h"

class MyBase : public gecko::ISerialisable
{
public:
	RTTI_DECL(MyBase)

	BEGIN_DATAMAP(MyBase, gecko::ISerialisable)
		REGISTER_FLOAT(m_myChar)
	END_DATAMAP

	char m_myChar;
};
RTTI_IMPL(MyBase, gecko::ISerialisable)

class MyClass : public MyBase
{
public:
	RTTI_DECL(MyClass)

	BEGIN_DATAMAP(MyClass, MyBase)
		REGISTER_STRING(m_string, 32)
		REGISTER_SHORT(m_myInt1)
		REGISTER_INT32(m_myInt2)
		REGISTER_FLOAT(m_myFloat2)
		REGISTER_FLOAT(m_myFloat)
	END_DATAMAP

	float m_myFloat;
	float m_myFloat2;
	short m_myInt1;
	int m_myInt2;
	char m_string[32];
};
RTTI_IMPL(MyClass, MyBase)

REGISTER_WITH_FACTORY(gecko::IObject, MyClass);

typedef gecko::StrongPtr<gecko::ISerialisable> SerialisablePtr;
typedef gecko::StrongPtr<MyClass> MyClassPtr;

int main()
{
	// smart pointer tests ///////////////////////////////////////////////////
	int * p = new int;

	{
		gecko::StrongPtr<int> i(p);
		gecko::StrongPtr<int> i2 = i;
		gecko::StrongPtr<int> i3 = i2;
		gecko::WeakPtr<int> w = i2;

		*i = 100;
	}
	//////////////////////////////////////////////////////////////////////////

	
	// factory tests //////////////////////////////////////////////////////////
	gecko::Factory<Base>::Get().Register<Derived>("Derived");
	gecko::Factory<Base>::Get().Register<Derived2>("Derived2");
	{
		{
			BasePtr b = gecko::Factory<Base>::Get().Create("Derived");
			b;
		}

		{
			BasePtr c = gecko::Factory<Base>::Get().Create("Derived2");
			c;
		}
	}
	//////////////////////////////////////////////////////////////////////////


	// memory pool tests /////////////////////////////////////////////////////
	gecko::MemoryPool<Base> basePool;
	{
		Base * b = basePool.Alloc();
		basePool.Free(b);
	}
	{
		Base * b = basePool.Alloc();
		basePool.Free(b);
	}
	{
		Base * b = basePool.Alloc();
		basePool.Free(b);
	}
	{
		Base * b = basePool.Alloc();
		basePool.Free(b);
	}
	{
		Base * b = basePool.Alloc();
		basePool.Free(b);
	}
	//////////////////////////////////////////////////////////////////////////

	// job manager test //////////////////////////////////////////////////////
	gecko::JobManager jobManager(10);

	for (int i = 0; i < 10; i++)
	{

		MyJob myJob1(1);
		MyJob myJob2(2);
		MyJob myJob3(3);
		MyJob myJob4(4);
		jobManager.AddJobToQueue(&myJob1);
		jobManager.AddJobToQueue(&myJob2);
		jobManager.AddJobToQueue(&myJob3);
		jobManager.AddJobToQueue(&myJob4);

		unsigned long start = gecko::GetMilliseconds();
		jobManager.ProcessJobs();
		unsigned long end = gecko::GetMilliseconds();

		DebugPrint("Jobs took %ums", (unsigned long)(end - start));
	}

	jobManager.Destroy();
	//////////////////////////////////////////////////////////////////////////

	// serialisation test ////////////////////////////////////////////////////
	{
		// declare a couple of classes
		MyClassPtr c1 = new MyClass;
		c1->m_myFloat = 1234.5f;
		c1->m_myFloat2 = 4567.8f;
		c1->m_myInt1 = 123;
		c1->m_myInt2 = 345;
		c1->m_myChar = 10;
		sprintf(c1->m_string, "Cocks");

		MyClassPtr c2 = new MyClass;
		c2->m_myFloat = 7845.5f;
		c2->m_myFloat2 = 3214.8f;
		c2->m_myInt1 = 789;
		c2->m_myInt2 = 654;
		c2->m_myChar = 20;
		sprintf(c2->m_string, "Balls");

		// push them into list
		gecko::SerialisableList objectList;
		objectList.PushBack(c1);
		objectList.PushBack(c2);

		// open output stream
		gecko::FileStream outStream("test.dat", gecko::IStream::ModeWrite);

		// serialise out to file
		gecko::Serialiser serialiser;
		serialiser.SerialiseObjects(outStream, objectList);
		outStream.Close();

		// clear the list
		objectList.Clear();

		// open input stream
		gecko::FileStream inStream("test.dat", gecko::IStream::ModeRead);

		// serialise back in
		serialiser.SerialiseObjects(inStream, objectList);

		// clear the list again
		inStream.Close();
	}
	//////////////////////////////////////////////////////////////////////////

	// Guid test /////////////////////////////////////////////////////////////
	//////////////////////////////////////////////////////////////////////////
	{
		// create a set, thereby if we get duplicates the set will be smaller than what we push into it
		std::set<gecko::Guid> guidSet;

		const int numGuids = 100000;
		for (int i = 0; i < numGuids; i++)
		{
			guidSet.insert(gecko::Guid());
		}

		int setSize = guidSet.size();

		Assert(setSize == numGuids, "Guid has come up with duplicates");
	}

	system("pause");
	return 0;
}
